import logging
import os
import re
import xml.etree.ElementTree as ET

from config import Config

logger = logging.getLogger("bgw")


class ConfigManager:
    """配置文件管理器"""

    def __init__(self):
        # 配置文件路径 - 使用 config.py 中的配置
        self.config_files = {
            'proxy': Config.proxy_xml_path,
            'sctp': Config.sctp_xml_path,
            'scp': Config.scp_xml_path,
            'sdp': Config.sdp_xml_path
        }

        # 参数映射：参数名 -> [(文件类型, XML路径), ...]
        # 支持一个参数在多个文件中的多个位置
        self.param_mapping = {
            '__GNB_IP__': [
                ('sctp', './/appSettings[@name="amf1"]/add[@key="LocalIP"]'),
                ('scp', './/AGAMFSCTPLinkConfig/LocalIP1'),
                ('scp', './/OperatorN3IpAddrConfig/AsGnbIP'),
                ('sdp', './/appSettings[@name="UPF"]/add[@key="LocalIp"]')
            ],
            '__AMF_IP__': [
                ('proxy', './/appSettings[@name="NgCfg"]/add[@key="N2IP"]'),
                ('sctp', './/appSettings[@name="amf1"]/add[@key="PeerIP"]'),
                ('scp', './/AGAMFParamConfig/AMFIP1'),
                ('scp', './/AGAmfSctpIPAddrStatus/AMFIP1'),
                ('scp', './/AGAmfSctpIPAddrStatus/AMFIP2')
            ],
            '__UPF_IP__': [
                ('proxy', './/appSettings[@name="NgCfg"]/add[@key="N3IP"]'),
                ('scp', './/OperatorN3IpAddrConfig/AsUpfIP')
            ],
            '__MCC__': [
                ('scp', './/AGBroadcastPLMNTable5G/MCC'),
                ('scp', './/AGSliceConfigList/MCC')
            ],
            '__MNC__': [
                ('scp', './/AGBroadcastPLMNTable5G/MNC'),
                ('scp', './/AGSliceConfigList/MNC')
            ],
            '__TAC__': [
                ('scp', './/AGTrackingAreaTable5G/TAC_HEX')  # 16进制
            ],
            '__IPSEC_IP__': [
                ('sctp', './/appSettings[@name="NgCfg"]/add[@key="N2IP"]'),
                ('sdp', './/appSettings[@name="N3"]/add[@key="LocalIp"]')
            ]
        }

    def get_param_value(self, param_name):
        """获取参数值 - 从第一个可用的位置获取"""
        try:
            if param_name not in self.param_mapping:
                return None

            # 遍历所有位置，返回第一个找到的值
            for file_key, xpath in self.param_mapping[param_name]:
                file_path = self.config_files.get(file_key)

                if not file_path or not os.path.exists(file_path):
                    continue

                tree = ET.parse(file_path)
                root = tree.getroot()

                element = root.find(xpath)
                if element is not None:
                    # 对于 XML 元素，获取文本内容；对于属性，获取属性值
                    if element.text and element.text.strip():
                        return element.text.strip()
                    elif element.get('value'):
                        return element.get('value')

            return None
        except Exception as e:
            logger.error(f"Error getting parameter {param_name}: {e}")
            return None

    def set_param_value(self, param_name, value):
        """设置参数值 - 同步更新所有相关位置"""
        try:
            if param_name not in self.param_mapping:
                return False

            success_count = 0
            total_locations = len(self.param_mapping[param_name])

            # 特殊处理 TAC 参数（16进制转换）
            if param_name == '__TAC__':
                try:
                    # 将十进制转换为16进制
                    hex_value = hex(int(value))[2:].upper()
                    value = hex_value
                except ValueError:
                    logger.error(f"Error: TAC value must be a valid number")
                    return False

            # 遍历所有位置进行更新
            for file_key, xpath in self.param_mapping[param_name]:
                file_path = self.config_files.get(file_key)

                if not file_path or not os.path.exists(file_path):
                    logger.warning(f"Warning: File {file_path} not found, skipping")
                    continue

                try:
                    tree = ET.parse(file_path)
                    root = tree.getroot()

                    element = root.find(xpath)
                    if element is not None:
                        # 检查元素是否已经有 value 属性（如 appSettings 中的 add 元素）
                        if element.get('value') is not None:
                            # 对于有 value 属性的元素，设置属性值
                            element.set('value', str(value))
                        else:
                            # 对于普通 XML 元素，设置文本内容
                            element.text = str(value)
                        tree.write(file_path, encoding='utf-8', xml_declaration=True)
                        success_count += 1
                        logger.info(f"Updated {param_name} in {file_key} at {xpath}")
                    else:
                        logger.warning(f"Warning: Element not found in {file_key} at {xpath}")

                except Exception as e:
                    logger.error(f"Error updating {param_name} in {file_key}: {e}")
                    continue

            # 返回是否至少有一个位置更新成功
            return success_count > 0

        except Exception as e:
            logger.error(f"Error setting parameter {param_name}: {e}")
            return False

    def get_all_params(self):
        """获取所有配置参数"""
        config = {}
        for param_name in self.param_mapping.keys():
            config[param_name] = self.get_param_value(param_name) or ''
        return config

    def update_params(self, params_dict):
        """批量更新配置参数"""
        results = {}
        amf_ip = None
        upf_ip = None

        for param_name, value in params_dict.items():
            if param_name in self.param_mapping:
                success = self.set_param_value(param_name, str(value))
                results[param_name] = success

                # 特殊处理：当更新 __GNB_IP__ 时，同步更新 set_ip.sh 文件中的 enp4s0 地址
                if param_name == '__GNB_IP__' and success:
                    self._update_set_ip_script(str(value))

                # 收集 AMF_IP 和 UPF_IP 参数，稍后一起更新 set_bgw_ns.sh
                if param_name == '__AMF_IP__' and success:
                    amf_ip = str(value)
                elif param_name == '__UPF_IP__' and success:
                    upf_ip = str(value)
            else:
                results[param_name] = False

        # 特殊处理：当更新 __AMF_IP__ 或 __UPF_IP__ 时，同步更新 set_bgw_ns.sh 文件
        if amf_ip is not None or upf_ip is not None:
            self._update_set_bgw_ns_script(amf_ip, upf_ip)

        return results

    def _update_set_ip_script(self, gnb_ip):
        """更新 set_ip.sh 文件中的 enp4s0 地址"""
        try:
            script_path = Config.set_ip_script_path
            if not os.path.exists(script_path):
                logger.warning(f"Warning: set_ip.sh file not found at {script_path}")
                return False

            # 读取文件内容
            with open(script_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()

            # 更新 enp4s0 相关的行（严格匹配，不匹配虚拟网卡如enp4s0:n3）
            updated = False
            for i, line in enumerate(lines):
                if not line.strip().startswith('#'):
                    # 使用正则表达式严格匹配 ifconfig enp4s0 后面跟空格或行尾
                    # 匹配模式：ifconfig enp4s0 后面必须是空格，不能是冒号（虚拟网卡）
                    if re.match(r'^\s*ifconfig\s+enp4s0\s+', line.strip()):
                        # 提取IP地址部分，保持子网掩码
                        parts = line.strip().split()
                        if len(parts) >= 3:
                            # 格式：ifconfig enp4s0 IP/SUBNET up
                            # 提取子网掩码部分
                            old_ip_part = parts[2]
                            if '/' in old_ip_part:
                                subnet = old_ip_part.split('/')[1]
                                new_ip_part = f"{gnb_ip}/{subnet}"
                            else:
                                new_ip_part = gnb_ip

                            lines[i] = f"ifconfig enp4s0 {new_ip_part} up\n"
                            updated = True
                            logger.info(f"Updated enp4s0 IP in set_ip.sh to {new_ip_part}")
                            break

            if updated:
                # 写回文件
                with open(script_path, 'w', encoding='utf-8') as f:
                    f.writelines(lines)
                return True
            else:
                logger.warning("Warning: No enp4s0 line found in set_ip.sh")
                return False

        except Exception as e:
            logger.error(f"Error updating set_ip.sh: {e}")
            return False

    @staticmethod
    def get_param(param_name):
        """
        从 config_result_file_path 文件中读取配置参数
        
        Args:
            param_name (str): 参数名称
            
        Returns:
            str: 参数值，如果参数不存在或文件不存在则返回 None
        """
        try:
            config_file_path = Config.config_result_file_path

            # 检查文件是否存在
            if not os.path.exists(config_file_path):
                logger.warning(f"Warning: Config file not found at {config_file_path}")
                return None

            # 读取配置文件
            with open(config_file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()

            # 解析键值对格式
            for line in lines:
                line = line.strip()
                # 跳过空行和注释行
                if not line or line.startswith('#'):
                    continue

                # 解析键值对格式：key: value
                if ':' in line:
                    key, value = line.split(':', 1)
                    key = key.strip()
                    value = value.strip()

                    if key == param_name:
                        logger.info(f"Found parameter {param_name}: {value}")
                        return value

            # 参数未找到
            logger.warning(f"Parameter {param_name} not found in config file")
            return None

        except Exception as e:
            logger.error(f"Error reading parameter {param_name} from config file: {e}")
            return None

    @staticmethod
    def get_all_param():
        """
        从 config_result_file_path 文件中读取所有配置参数

        Returns:
            dict: 包含所有参数的字典，如果文件不存在则返回空字典
        """
        try:
            config_file_path = Config.config_result_file_path

            # 检查文件是否存在
            if not os.path.exists(config_file_path):
                logger.warning(f"Warning: Config file not found at {config_file_path}")
                return {}

            # 读取配置文件
            with open(config_file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()

            # 解析所有键值对
            params = {}
            for line in lines:
                line = line.strip()
                # 跳过空行和注释行
                if not line or line.startswith('#'):
                    continue

                # 解析键值对格式：key: value
                if ':' in line:
                    key, value = line.split(':', 1)
                    key = key.strip()
                    value = value.strip()
                    params[key] = value

            logger.info(f"Loaded {len(params)} parameters from config file")
            return params

        except Exception as e:
            logger.error(f"Error reading all parameters from config file: {e}")
            return {}

    def _update_set_bgw_ns_script(self, amf_ip=None, upf_ip=None):
        """更新 set_bgw_ns.sh 文件中的 OPER_AMF_IP 和 OPER_UPF_IP"""
        try:
            script_path = Config.set_bgw_ns_script_path
            if not os.path.exists(script_path):
                logger.warning(f"Warning: set_bgw_ns.sh file not found at {script_path}")
                return False

            # 读取文件内容
            with open(script_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()

            # 更新 OPER_AMF_IP 和 OPER_UPF_IP 行
            updated_amf = False
            updated_upf = False

            for i, line in enumerate(lines):
                # 更新 OPER_AMF_IP
                if amf_ip and line.strip().startswith('OPER_AMF_IP='):
                    # 提取子网掩码部分（如果存在）
                    old_value = line.strip().split('=')[1]
                    if '/' in old_value:
                        subnet = old_value.split('/')[1]
                        new_value = f"{amf_ip}/{subnet}"
                    else:
                        new_value = amf_ip

                    lines[i] = f"OPER_AMF_IP={new_value}\n"
                    updated_amf = True
                    logger.info(f"Updated OPER_AMF_IP in set_bgw_ns.sh to {new_value}")

                # 更新 OPER_UPF_IP
                if upf_ip and line.strip().startswith('OPER_UPF_IP='):
                    # 提取子网掩码部分（如果存在）
                    old_value = line.strip().split('=')[1]
                    if '/' in old_value:
                        subnet = old_value.split('/')[1]
                        new_value = f"{upf_ip}/{subnet}"
                    else:
                        new_value = upf_ip

                    lines[i] = f"OPER_UPF_IP={new_value}\n"
                    updated_upf = True
                    logger.info(f"Updated OPER_UPF_IP in set_bgw_ns.sh to {new_value}")

            if updated_amf or updated_upf:
                # 写回文件
                with open(script_path, 'w', encoding='utf-8') as f:
                    f.writelines(lines)
                return True
            else:
                if amf_ip and not updated_amf:
                    logger.warning("Warning: No OPER_AMF_IP line found in set_bgw_ns.sh")
                if upf_ip and not updated_upf:
                    logger.warning("Warning: No OPER_UPF_IP line found in set_bgw_ns.sh")
                return False

        except Exception as e:
            logger.error(f"Error updating set_bgw_ns.sh: {e}")
            return False
